﻿// Sintatico.cs created with MonoDevelop
// User: renato at 22:05 1/7/2008
//
// To change standard headers go to Edit->Preferences->Coding->Standard Headers
//

using System;
using System.Collections.Generic;

namespace compilador
{
    public class ModoPanico
    {
        Lexico lex;
        public ModoPanico(Lexico l)
        {
            lex = l;
        }

        public ItemSimbolo Erro(ItemSimbolo tokAtual, params TipoToken[] sync)
        {
            // comentar essa linha pra não ignorar o token atual
            // tokAtual = lex.proximoToken(true);
            while (Array.IndexOf(sync, tokAtual.Token) == -1)
                tokAtual = lex.proximoToken(true);
            return tokAtual;
        }

        public TipoToken[] ConcatArray(TipoToken[] arr1, params TipoToken[] arr2)
        {
            TipoToken[] cat = new TipoToken[arr1.Length + arr2.Length];
            arr1.CopyTo(cat, 0);
            arr2.CopyTo(cat, arr1.Length);
            return cat;
        }
    }

    public class Sintatico
    {
        int MemProcedure = 0; // quantidade de memória alocada por uma procedure;
        Stack<ItemSimboloTS> PilhaSintatica = new Stack<ItemSimboloTS>();
//        Stack<ItemSimboloTS> PilhaCondicao = new Stack<ItemSimboloTS>();

        TipoToken[] SyncPadrao = { };
        Lexico lex;
        ItemSimbolo i;
        TipoToken[] cmds = {TipoToken.t_read,
			TipoToken.t_write,
			TipoToken.t_identificador,
			TipoToken.t_if,
			TipoToken.t_while,
			TipoToken.t_begin};
        Singleton s = Singleton.Instancia;
        ModoPanico mp;
        bool Declaracao = false; //setar para true quando for declaração de variáveis
        bool EstaEmProcedure = false; // informa ao parser dos comandos se está analizando comandos de uma procedure
        TipoToken TipoVariavel = TipoToken.t_nenhum; // armazenar o tipo da variavel quando for descoberto
        Simbolos TabSimbolos;
        TipoToken TipoExpressao = TipoToken.t_nenhum;
        TipoToken UltimoOperador = TipoToken.t_nenhum;
        TipoToken UltimoComando = TipoToken.t_nenhum;
        TipoToken OperadorRelacional = TipoToken.t_nenhum;
        string strExpressao = "";

        public Sintatico(Lexico l, Simbolos s)
        {
            lex = l;
            mp = new ModoPanico(lex);
            TabSimbolos = s;
        }
        public void programa(params TipoToken[] S)
        {
            TabSimbolos.AdicionaTokens = false;
            CodMaquina.Clear();
            CodMaquina.InsereCodigo("INPP");
            TabSimbolos.AssignMem = false;
            i = lex.proximoToken(true);

            if (i.Token != TipoToken.t_program)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \"program\" esperado.");
                i = mp.Erro(i, mp.ConcatArray(S, TipoToken.t_identificador,
                    //TipoToken.t_pontoevirgula,
                    //Primeiros de CORPO
                                              TipoToken.t_begin,
                                              TipoToken.t_var,
                                              TipoToken.t_procedure
                                             ));
            }
            else
            {
                TabSimbolos.AdicionaTokens = true;
                TabSimbolos.isDecNomePrograma = true;
                i = lex.proximoToken(true);
                TabSimbolos.isDecNomePrograma = false;
                TabSimbolos.AdicionaTokens = false;

            }



            if (i.Token != TipoToken.t_identificador)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: identificador esperado.");
                i = mp.Erro(i, mp.ConcatArray(S, TipoToken.t_pontoevirgula,
                                                TipoToken.t_identificador,
                    //Primeiros de CORPO
                                              TipoToken.t_begin,
                                              TipoToken.t_var,
                                              TipoToken.t_procedure
                                             ));
            }
            else i = lex.proximoToken(true);



            if (i.Token != TipoToken.t_pontoevirgula)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \";\" esperado.");
                i = mp.Erro(i, mp.ConcatArray(S,//Primeiros de CORPO
                                              TipoToken.t_pontoevirgula,
                                              TipoToken.t_begin,
                                              TipoToken.t_var,
                                              TipoToken.t_procedure));
            }

            // chamo o método CORPO passando o seguidor de CORPO
            this.corpo(TipoToken.t_ponto);

            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_ponto)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \".\" esperado.");
            }
        }// programa

        private void corpo(params TipoToken[] S)
        {
            this.declaracoes(TipoToken.t_begin);
            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_begin)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \"begin\" esperado");
            }
            TabSimbolos.AdicionaTokens = false;
            TabSimbolos.Escopo = 0;
            this.comandos();

            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_end)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \"end\" esperado");
            }

        }//corpo
        private void declaracoes(params TipoToken[] S)
        {

            this.decVariaveis(S);
            this.decProcedures(S);
        }//declaracoes

        private void decVariaveis(params TipoToken[] S)
        {
            TabSimbolos.AssignMem = true;
            TabSimbolos.AdicionaTokens = true;
            i = lex.proximoToken(false);
            if (i.Token == TipoToken.t_var)
            {
                lex.proximoToken(true);
                Declaracao = true;
                this.variaveis(mp.ConcatArray(S, TipoToken.t_doispontos));
                Declaracao = false;

                i = lex.proximoToken(true);
                if (i.Token != TipoToken.t_doispontos)
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO: \":\" esperado");
                }

                TipoVariavel = TipoToken.t_nenhum;
                this.tipoVariavel(mp.ConcatArray(S, TipoToken.t_pontoevirgula));

                i = lex.proximoToken(true);
                if (i.Token != TipoToken.t_pontoevirgula)
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO: \";\" esperado");
                }
                else
                {   // pilha de variáveis reconhecidas
                    while (PilhaSintatica.Count > 0)
                    {
                        string s = (String)PilhaSintatica.Pop().Lexema;
                        TabSimbolos.AlteraTipo(s, TipoVariavel);
                        CodMaquina.InsereCodigo("ALME", 1);
                    }
                }
                this.decVariaveis(S);
            }

        }

        private void tipoVariavel(params TipoToken[] S)
        {
            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_tipodado)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: Tipo esperado, encontrado->" + i.Token);
            }
            else
            {
                if (i.Lexema.ToLower().Equals("integer"))
                    TipoVariavel = TipoToken.t_numint;
                else
                    TipoVariavel = TipoToken.t_numreal;
            }
        }

        private void variaveis(params TipoToken[] S)
        {
            //Console.WriteLine("I - Parseando variaveis");
            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_identificador)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: identificador esperado");
            }
            else
            {
                if (Declaracao)
                {
                    PilhaSintatica.Push(new ItemSimboloTS(i.Token,
                                                          i.Lexema,
                                                          TabSimbolos.ObtemPosMem(i.Lexema),
                                                           TipoToken.t_nenhum));
                    if (EstaEmProcedure)
                        MemProcedure++;
                }

                switch (UltimoComando)
                {
                    case TipoToken.t_read:
                        CodMaquina.InsereCodigo("LEIT");
                        CodMaquina.InsereCodigo("ARMZ", TabSimbolos.ObtemPosMem(i.Lexema));
                        if (TipoVariavel.Equals(TipoToken.t_nenhum))
                            TipoVariavel = TabSimbolos.ObtemTipo(i.Lexema);
                        else if (!TipoVariavel.Equals(TabSimbolos.ObtemTipo(i.Lexema)))
                        {
                            s.deuErro = true;
                            Console.WriteLine("ERRO: Parametros de tipos diferentes no comando read");
                            Environment.Exit(1);
                        }
                           
                        break;
                    case TipoToken.t_write:
                        CodMaquina.InsereCodigo("CRVL", TabSimbolos.ObtemPosMem(i.Lexema));
                        CodMaquina.InsereCodigo("IMPR");
                        if (TipoVariavel.Equals(TipoToken.t_nenhum))
                            TipoVariavel = TabSimbolos.ObtemTipo(i.Lexema);
                        else if (!TipoVariavel.Equals(TabSimbolos.ObtemTipo(i.Lexema)))
                        {
                            s.deuErro = true;
                            Console.WriteLine("ERRO: Parametros de tipos diferentes no comando read");
                            Environment.Exit(1);
                        }
                        break;
                }

                this.maisVariaveis(S);
            }

        }
        private void maisVariaveis(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token == TipoToken.t_virgula)
            {
                lex.proximoToken(true);
                this.variaveis(S);
            }// else não faz nada
        }
        private void decProcedures(params TipoToken[] S)
        {
            EstaEmProcedure = true;
            MemProcedure = 0;
            TabSimbolos.RestauraProcedure();// restaura o endereçamento da mamória
            //Console.WriteLine("Dc Procedures");
            i = lex.proximoToken(false);
            if (i.Token == TipoToken.t_procedure)
            {
                int desv = CodMaquina.InsereCodigo("DSVI");
                lex.proximoToken(true);// consome o t_procedure
                TabSimbolos.isProcedure = true;
                TabSimbolos.AdicionaTokens = true;
                i = lex.proximoToken(true);
                
                if (i.Token != TipoToken.t_identificador)
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO: identificador esperado");
                }
                else
                {

                    TabSimbolos.AlteraPosMem(i.Lexema, CodMaquina.ObtemPosicaoArquivo() + 1);
                    TabSimbolos.Escopo++;
                    TabSimbolos.isDecProcedure = true;
                    this.parametros(mp.ConcatArray(S, TipoToken.t_pontoevirgula));
                    TabSimbolos.isProcedure = false;
                    i = lex.proximoToken(true);

                    if (i.Token != TipoToken.t_pontoevirgula)
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO: \";\" esperado");
                    }
                    else
                    {// se tiver o pontoevirgula
                        this.corpoProcedure(S);
                        CodMaquina.InsereCodigo("DESM", MemProcedure);
                        CodMaquina.InsereCodigo("RTPR");
                        CodMaquina.AlteraComando(desv, "DSVI", CodMaquina.ObtemPosicaoArquivo() + 1);
                        this.decProcedures(S);
                    }
                }
            }
            EstaEmProcedure = false;
        }
        private void parametros(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token == TipoToken.t_abrepar)
            {
                lex.proximoToken(true);

                this.listaDeParametros(mp.ConcatArray(S, TipoToken.t_fechapar));

                i = lex.proximoToken(true);
                if (i.Token != TipoToken.t_fechapar)
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO: \")\" esperado");
                }


            }//se for abra parenteses, tem parametros, senão não tem nada
        }
        private void listaDeParametros(params TipoToken[] S)
        {
            Declaracao = true;
            this.variaveis(mp.ConcatArray(S, TipoToken.t_doispontos));
            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_doispontos)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \":\" esperado");
            }
            else
            {
                TipoVariavel = TipoToken.t_nenhum;
                this.tipoVariavel(S);
                while (PilhaSintatica.Count > 0)
                {
                    string s = (String)PilhaSintatica.Pop().Lexema;
                    TabSimbolos.AlteraTipo(s, TipoVariavel);
                    //CodMaquina.InsereCodigo("ALME", 1);
                }
                this.maisParametros(S);
            }

        }
        private void maisParametros(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token == TipoToken.t_pontoevirgula)
            {
                this.listaDeParametros(S);
            }
        }
        private void corpoProcedure(params TipoToken[] S)
        {
            TabSimbolos.AdicionaTokens = true;
            TabSimbolos.isDecProcedure = true;
            this.decVariaveis(mp.ConcatArray(S, TipoToken.t_begin));
            TabSimbolos.isDecProcedure = false;
            TabSimbolos.AdicionaTokens = false;
            i = lex.proximoToken(true);
            if (i.Token != TipoToken.t_begin)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: \"begin\" esperado");
            }
            else
            {
                this.comandos(mp.ConcatArray(S, TipoToken.t_end));
                i = lex.proximoToken(true);
                if (i.Token != TipoToken.t_end)
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO: \"end\" esperado");
                }
                else
                {
                    i = lex.proximoToken(true);
                    if (i.Token != TipoToken.t_pontoevirgula)
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO: \";\" esperado");
                    }
                }
            }
            EstaEmProcedure = false;
        }// corpoprocedure

        private void comandos(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (!Array.IndexOf(cmds, i.Token).Equals(-1))
            {
                this.comando(mp.ConcatArray(S, TipoToken.t_pontoevirgula));
                i = lex.proximoToken(false);
                if (i.Token == TipoToken.t_pontoevirgula)
                {
                    i = lex.proximoToken(true); // consumir o ;
                    this.comandos(S);
                    //Console.WriteLine("voltou para comandos...");
                }
            }
        }
        private void comando(params TipoToken[] S)
        {
            TabSimbolos.AdicionaTokens = false;
            i = lex.proximoToken(false);
            PilhaSintatica.Clear();
            switch (i.Token)
            {
                case TipoToken.t_read:
                    lex.proximoToken(true); // consumir o read
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_abrepar))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \"(\" esperado");
                    }
                    UltimoComando = TipoToken.t_read;
                    TipoVariavel = TipoToken.t_nenhum;
                    this.variaveis(mp.ConcatArray(S, TipoToken.t_fechapar));
                    UltimoComando = TipoToken.t_nenhum;
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_fechapar))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \")\" esperado");
                    }
                    break;
                case TipoToken.t_write:
                    lex.proximoToken(true); // consumir o write
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_abrepar))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \"(\" esperado");
                    }
                    UltimoComando = TipoToken.t_write;
                    TipoVariavel = TipoToken.t_nenhum;
                    this.variaveis(mp.ConcatArray(S, TipoToken.t_fechapar));
                    UltimoComando = TipoToken.t_nenhum;
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_fechapar))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \")\" esperado");
                    }
                    break;
                case TipoToken.t_while:
                    lex.proximoToken(true); // consumir o while
                    int inicioCondicao = CodMaquina.ObtemPosicaoArquivo() + 1;
                    this.condicao(S);
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_do))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \"do\" esperado");
                    }
                    CodMaquina.InsereCodigo("DSVF", -1);
                    int indDesv = CodMaquina.ObtemPosicaoArquivo();
                    this.comando(S);                    
                    CodMaquina.InsereCodigo("DSVI", inicioCondicao);
                    CodMaquina.AlteraComando(indDesv, "DSVF", CodMaquina.ObtemPosicaoArquivo() + 1);
                    //Console.WriteLine("Saiu do While");
                    break;
                case TipoToken.t_if:
                    lex.proximoToken(true); // consumir o if
                    this.condicao(mp.ConcatArray(S, TipoToken.t_then));
                    int indDesv1 = CodMaquina.InsereCodigo("DSVF", -1);
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_then))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \"then\" esperado");
                    }
                    this.comando(mp.ConcatArray(S, TipoToken.t_else));
                    int indDesv2 = CodMaquina.InsereCodigo("DSVI", -1);
                    CodMaquina.AlteraComando(indDesv1, "DSVF", CodMaquina.ObtemPosicaoArquivo() + 1);
                    this.pfalsa(S);
                    CodMaquina.AlteraComando(indDesv2, "DSVI", CodMaquina.ObtemPosicaoArquivo() + 1);
                    break;
                case TipoToken.t_identificador:
                    i = lex.proximoToken(true); // consome o identificador
                    string identificador = i.Lexema;
                    i = lex.proximoToken(false);
                    if (i.Token.Equals(TipoToken.t_doispontosigual))
                    {
                        strExpressao = identificador + ":=";
                        i = lex.proximoToken(true); // consome a atribuição (:=)                   
                        this.expressao(S);
                        TipoToken t = TabSimbolos.ObtemTipo(identificador);
                        if (!t.Equals(TipoExpressao) && (TipoExpressao == TipoToken.t_numreal))
                        {
                            Console.WriteLine("Erro Semântico: Tipos incompatíveis ->  " + strExpressao);
                            Console.WriteLine("Esperado: Integer  Encontrado: Real");
                            s.deuErro = true;
                            Environment.Exit(1);
                        }
                        CodMaquina.InsereCodigo("ARMZ", TabSimbolos.ObtemPosMem(identificador));
                        //Console.WriteLine("voltou para comando...");
                    }
                    else
                    {
                        if (EstaEmProcedure)
                        {
                            s.deuErro = true;
                            Console.WriteLine("ERRO:  metodos só podem ser chamados no corpo do programa");
                            Environment.Exit(1);
                        }
                        int indPusher = CodMaquina.InsereCodigo("PUSHER");
                        this.listaArgumentos(S);
                        Stack<ItemSimboloTS> pilhaAux = new Stack<ItemSimboloTS>();
                        while (PilhaSintatica.Count > 0)
                        {
                            pilhaAux.Push(PilhaSintatica.Pop());
                        }
                        while (pilhaAux.Count > 0)
                        {
                            CodMaquina.InsereCodigo("PARAM", TabSimbolos.ObtemPosMem(pilhaAux.Pop().Lexema));
                        }
                        CodMaquina.InsereCodigo("CHPR", TabSimbolos.ObtemPosMem(identificador));
                        CodMaquina.AlteraComando(indPusher, "PUSHER", CodMaquina.ObtemPosicaoArquivo() + 1);

                    }
                    break;
                case TipoToken.t_begin:
                    i = lex.proximoToken(true); // Consome o begin
                    this.comandos(mp.ConcatArray(S, TipoToken.t_end));
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_end))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO:  \"end\" esperado");
                    }
                    break;

            }
        }

        private void maisIdent(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token.Equals(TipoToken.t_pontoevirgula))
            {
                lex.proximoToken(true);
                this.argumentos(S);
            }
        }

        private void argumentos(params TipoToken[] S)
        {
            i = lex.proximoToken(true);
            if (!i.Token.Equals(TipoToken.t_identificador))
            {
                s.deuErro = true;
                Console.WriteLine("ERRO:  identificador esperado");
            }
            else
            {
                PilhaSintatica.Push(new ItemSimboloTS(i.Token,
                                                      i.Lexema,
                                                      TabSimbolos.ObtemPosMem(i.Lexema),
                                                      TabSimbolos.ObtemTipo(i.Lexema)));
                this.maisIdent(S);
            }
        }

        private void listaArgumentos(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token.Equals(TipoToken.t_abrepar))
            {
                lex.proximoToken(true); // consumir o (

                this.argumentos(mp.ConcatArray(S, TipoToken.t_fechapar));

                i = lex.proximoToken(true);
                if (!i.Token.Equals(TipoToken.t_fechapar))
                {
                    s.deuErro = true;
                    Console.WriteLine("ERRO:  \")\" esperado");
                }
            }
        }

        private void pfalsa(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            if (i.Token.Equals(TipoToken.t_else))
            {
                lex.proximoToken(true);// consome o else
                this.comando(S);
            }
        }

        private void condicao(params TipoToken[] S)
        {
//            PilhaCondicao.Clear();
            OperadorRelacional = TipoToken.t_nenhum;
            this.expressao(S);
            this.relacao(S);
            this.expressao(S);
            CodMaquina.GeraOperadorR(OperadorRelacional);
        }
        private void relacao(params TipoToken[] S)
        {
            i = lex.proximoToken(true);
            TipoToken[] rel = {TipoToken.t_igual,
				TipoToken.t_diferente,
				TipoToken.t_maior,
				TipoToken.t_menor,
				TipoToken.t_maiorigual,
				TipoToken.t_menorigual};
            if (Array.IndexOf(rel, i.Token) == -1)
            {
                s.deuErro = true;
                Console.WriteLine("ERRO: Operador Relacional esperado");
            }
            else
            {
                OperadorRelacional = i.Token;
            }

        }
        private void expressao(params TipoToken[] S)
        {
            TipoExpressao = TipoToken.t_numint;
            UltimoOperador = TipoToken.t_nenhum;
            PilhaSintatica.Clear();
            this.opun(S);
            this.termo(S);
            this.maisTermos(S);
            if (PilhaSintatica.Count > 0)
            {
                ItemSimboloTS i = PilhaSintatica.Pop();
                CodMaquina.GeraCR(i, TabSimbolos);
            }

        }
        private void opun(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            {
                if ((i.Token.Equals(TipoToken.t_soma)) || (i.Token.Equals(TipoToken.t_subtracao)))
                {
                    lex.proximoToken(true); // pra consumir o sinal..
                }
            }

        }
        private void termo(params TipoToken[] S)
        {
            //Console.WriteLine("Termo");
            this.fator(S);
            this.maisFatores(S);
        }
        private void maisTermos(params TipoToken[] S)
        {
            //Console.WriteLine("Mais termos");
            if (i.Token.Equals(TipoToken.t_soma) || i.Token.Equals(TipoToken.t_subtracao))
            {
                strExpressao = strExpressao + i.Lexema;
                TipoToken operacao = this.opad(S);
                this.termo(S);
                if (PilhaSintatica.Count > 0)
                {
                    ItemSimboloTS j = PilhaSintatica.Pop();
                    if (PilhaSintatica.Count > 0)
                    {
                        ItemSimboloTS k = PilhaSintatica.Pop();
                        CodMaquina.GeraCR(k, TabSimbolos);
                    }
                    CodMaquina.GeraCR(j, TabSimbolos);
                }
                /*while (PilhaSintatica.Count > 0)
                {
                    ItemSimboloTS t = PilhaSintatica.Pop();
                    if ((t.PosMemoria == -1) && ((t.Tipo.Equals(TipoToken.t_numint)) || (t.Tipo.Equals(TipoToken.t_numreal))))
                        CodMaquina.InsereCodigo("CRCT", Convert.ToDouble(t.Lexema));
                    else
                        CodMaquina.InsereCodigo("CRVL", t.PosMemoria);
                }*/
                switch (operacao)
                {
                    case TipoToken.t_soma:
                        CodMaquina.InsereCodigo("SOMA");
                        break;
                    case TipoToken.t_subtracao:
                        CodMaquina.InsereCodigo("SUBT");
                        break;
                }

                this.maisTermos();
            }
        }
        private void fator(params TipoToken[] S)
        {
            //Console.WriteLine("Fator");
            i = lex.proximoToken(true);
            strExpressao = strExpressao + i.Lexema;
            switch (i.Token)
            {
                case TipoToken.t_identificador:
                    if (TabSimbolos.ObtemTipo(i.Lexema) == TipoToken.t_numreal)
                        TipoExpressao = TipoToken.t_numreal;
                    PilhaSintatica.Push(new ItemSimboloTS(TipoToken.t_identificador,
                                                          i.Lexema,
                                                          TabSimbolos.ObtemPosMem(i.Lexema),
                                                          TabSimbolos.ObtemTipo(i.Lexema)
                                                          ));
                    break;
                case TipoToken.t_numint:
                    PilhaSintatica.Push(new ItemSimboloTS(TipoToken.t_numint,
                                                          i.Lexema,
                                                          -1,
                                                          TipoToken.t_numint
                                                          ));

                    break;
                case TipoToken.t_numreal:
                    PilhaSintatica.Push(new ItemSimboloTS(TipoToken.t_numint,
                                      i.Lexema,
                                      -1,
                                      TipoToken.t_numint
                                      ));
                    TipoExpressao = TipoToken.t_numreal;
                    break;
                case TipoToken.t_abrepar:
                    this.expressao(mp.ConcatArray(S, TipoToken.t_fechapar));
                    i = lex.proximoToken(true);
                    if (!i.Token.Equals(TipoToken.t_fechapar))
                    {
                        s.deuErro = true;
                        Console.WriteLine("ERRO: \")\" esperado");
                    }
                    break;
            }
        }
        private void maisFatores(params TipoToken[] S)
        {
            //Console.WriteLine("Mais Fatores");
            i = lex.proximoToken(false);
            if (i.Token.Equals(TipoToken.t_divisao) || i.Token.Equals(TipoToken.t_multiplicacao))
            {
                strExpressao = strExpressao + i.Lexema;
                TipoToken operacao = this.opmul(S);
                this.fator(S);

                if (PilhaSintatica.Count > 0)
                {
                    ItemSimboloTS j = PilhaSintatica.Pop();
                    if (PilhaSintatica.Count > 0)
                    {
                        ItemSimboloTS k = PilhaSintatica.Pop();
                        CodMaquina.GeraCR(k, TabSimbolos);
                    }
                    CodMaquina.GeraCR(j, TabSimbolos);
                }

                /*while (PilhaSintatica.Count > 0)
                {
                    ItemSimboloTS t = PilhaSintatica.Pop();
                    if (t.PosMemoria == -1)
                        CodMaquina.InsereCodigo("CRCT", Convert.ToDouble(t.Lexema));
                    else
                        CodMaquina.InsereCodigo("CRVL", t.PosMemoria);
                }
                */
                switch (operacao)
                {
                    case TipoToken.t_multiplicacao:
                        CodMaquina.InsereCodigo("MULT");
                        break;
                    case TipoToken.t_divisao:
                        CodMaquina.InsereCodigo("DIVI");
                        TipoExpressao = TipoToken.t_numreal;
                        break;
                }
                this.maisFatores(S);
            }
        }
        private TipoToken opad(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            UltimoOperador = i.Token;
            if (i.Token.Equals(TipoToken.t_soma))
            {
                lex.proximoToken(true);
                return TipoToken.t_soma;
            }

            if (i.Token.Equals(TipoToken.t_subtracao))
            {
                lex.proximoToken(true);                
                return TipoToken.t_subtracao;
            }
            s.deuErro = true;
            Console.WriteLine("ERRO: operador esperado");
            return TipoToken.t_nenhum;
        }
        private TipoToken opmul(params TipoToken[] S)
        {
            i = lex.proximoToken(false);
            UltimoOperador = i.Token;
            if (i.Token.Equals(TipoToken.t_multiplicacao))
            {
                lex.proximoToken(true);
                return TipoToken.t_multiplicacao;
            }

            if (i.Token.Equals(TipoToken.t_divisao))
            {
                lex.proximoToken(true);
                return TipoToken.t_divisao;
            }
            s.deuErro = true;
            Console.WriteLine("ERRO: operador esperado");
            return TipoToken.t_nenhum;
        }
    }
}
